#![allow(dead_code)]

mod gc;
use gc::GcAlloc;
use std::{
    ops::{Index, IndexMut},
    ptr,
};

#[derive(Clone, Copy)]
pub struct vec<T> {
    p: *mut T,
    len: usize,
    cap: usize,
}

impl<T: Copy> vec<T> {
    pub fn new() -> Self {
        Self {
            p: 0 as *mut T,
            len: 0,
            cap: 0,
        }
    }

    pub fn extend(&mut self) {
        unsafe {
            let new_cap = if self.cap == 0 { 1 } else { self.cap << 1 };
            self.p = if self.cap == 0 {
                GcAlloc::alloc_array(new_cap)
            } else {
                GcAlloc::realloc_array(self.p, self.cap, new_cap)
            };
            self.cap = new_cap;
        }
    }

    // Expose raw pointer for GC root marking
    pub fn as_ptr(&self) -> *const u8 {
        self.p as *const u8
    }

    fn copy(&self) -> vec<T> {
        let mut v: vec<T> = vec::new();
        while v.cap < self.cap {
            v.extend();
        }
        v
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }
    pub fn len(&self) -> usize {
        self.len
    }
    pub fn capacity(&self) -> usize {
        self.cap
    }

    pub fn push(&mut self, x: T) {
        unsafe {
            if self.len == self.cap {
                self.extend();
            }
            ptr::write(self.p.add(self.len), x);
            self.len += 1;
        }
    }
}

macro_rules! impl_index_for_int {
    ($($t:ty),*) => {
        $(
            impl<T> Index<$t> for vec<T> {
                type Output = T;
                fn index(&self, index: $t) -> &Self::Output {
                    unsafe { &*self.p.add(index as usize) }
                }
            }

            impl<T> IndexMut<$t> for vec<T>  {
                fn index_mut(&mut self, index: $t) -> &mut Self::Output {
                    unsafe { &mut*self.p.add(index as usize) }
                }
            }
        )*
    };
}

impl_index_for_int!(
    usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128
);
